package cn.com.wxd.service.dispatch.engine;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDataMap;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.com.wxd.common.GlobalConstant;
import cn.com.wxd.common.JsonUtil;
import cn.com.wxd.common.XmlUtil;
import cn.com.wxd.common.framedata.FrameDataUtil;

/**
 * Title:QuartzEngine 类，该类在启动时初始化，实现对定时任务队列的增删改查
 * Description:对当前已经启动的定时任务的增删改查，和初始化定时任务。
 * <p>
 * Company: Amumu管理平台
 * Copyright: Copyright (c) 2016
 * All right reserved.
 * Created time: 2017-2-24 上午11:41:19
 *
 * @author wangxudong
 * @version 2.0
 */
public class QuartzEngine {
    private static final Logger log = LoggerFactory.getLogger(QuartzEngine.class);
    private static QuartzEngine quartzEngine = null;
    private static final String DETAILGROUPNAME = "detailgroup_1";
    private static final String TIGGERGROUPNAME = "tiggergroup_1";
    private static final String DETAILNAMEPREFIX = "customtask_detail_";
    private static final String TIGGERNAMEPREFIX = "customtask_tigger_";

    private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();
    private static List<ScheduleModel> schedules;
    private static Scheduler scheduler;
    private static boolean isInit = false;

    private QuartzEngine() {
    }

    /**
     * 获取引擎实例，如果该引擎没有初始化，则现在自动初始化！
     *
     * @return
     */
    public static synchronized QuartzEngine getInstance() {
        if (quartzEngine == null) {
            try {
                quartzEngine = new QuartzEngine();
                scheduler = schedulerFactory.getScheduler();
                if (!isInit) {
                    quartzEngine.init();
                    isInit = true;
                }
            } catch (SchedulerException e) {
                log.error("〖customtask〗QuartzEngine引擎初始化失败!", e);
                quartzEngine = null;
            }//自动初始化引擎
        }
        return quartzEngine;
    }

    /**
     * 初始化Quartz引擎
     *
     * @throws SchedulerException
     */
    private void init() throws SchedulerException {
        log.info("customtask任务调度引擎Quartz开始初始化!");
        try {
            schedules = getScheduleModels();
        } catch (Exception e) {
            schedules = null;
        }
        if (schedules != null && schedules.size() > 0) {
            for (ScheduleModel scheduleModel : schedules) {
                try {
                    JobDetail jobDetail = builderJobDetail(scheduleModel);
                    Trigger trigger = null;
                    if (scheduleModel.getType().equalsIgnoreCase("CRON")) {
                        trigger = builderCronTrigger(scheduleModel); //构建cron表达式触发器
                    }
                    if (scheduleModel.getType().equalsIgnoreCase("SIMPLE")) {
                        trigger = builderSimpleTrigger(scheduleModel); //构建SIMPLE表达式触发器
                    }
                    if (scheduleModel.isRun() && trigger != null) {
                        scheduler.scheduleJob(jobDetail, trigger);//任务组合
                        log.debug("〖customtask〗计划任务" + scheduleModel.getTaskId() + "--"
                                    + "--" + scheduleModel.getTaskName() + "，准备就绪！");
                    } else if (trigger == null) {
                        log.warn("〖customtask〗计划任务" + scheduleModel.getTaskId() + "--"
                                + "--" + scheduleModel.getTaskName() + "，无法建立定时器！\n"
                                + JsonUtil.objectToJson(scheduleModel));
                    }
                } catch (Exception e) {
                    log.error("〖customtask〗QuartzEngine引擎启动任务--" + scheduleModel.getTaskId() + "--"
                            + "--" + scheduleModel.getTaskName() + "任务失败！无法使用定时任务功能！\n"
                            + JsonUtil.objectToJson(scheduleModel), e);

                }
            }
            scheduler.start();//开始
            log.info("〖customtask〗QuartzEngine引擎初始化成功!");
        } else {
            log.debug("〖customtask〗QuartzEngine引擎计划任务没有内容，不执行！");
        }
    }

    /**
     * 构建工作Detail
     *
     * @param scheduleModel
     *
     * @return
     */
    private JobDetail builderJobDetail(ScheduleModel scheduleModel) {
        JobDataMap jdm = new JobDataMap();
        jdm.put("TaskId", scheduleModel.getTaskId());
        jdm.put("Script", scheduleModel.getScript());
        jdm.put("TaskName", scheduleModel.getTaskName());
        JobDetail jobDetail;
        jobDetail = JobBuilder.newJob(QuartzEngineJob.class)
                .withIdentity(DETAILNAMEPREFIX + scheduleModel.getTaskId(), DETAILGROUPNAME)
                .setJobData(jdm)
                .build();
        //使用 qutrtz 2以下版本时候用如下构建方式
        //new JobDetail(DETAILNAMEPREFIX+scheduleModel.getTaskId(), DETAILGROUPNAME,
        //QuartzEngineJob.class);
        //jobDetail.setJobDataMap(jdm);
        return jobDetail;
    }

    /**
     * 构建一个corn表达式的触发器
     *
     * @param scheduleModel
     *
     * @return CornTrigger
     *
     * @throws ParseException
     */
    private Trigger builderCronTrigger(ScheduleModel scheduleModel) throws ParseException {
        if (scheduleModel.getType() != null && scheduleModel.getType().equalsIgnoreCase("CRON")
                && scheduleModel.getCron() != null
                && !scheduleModel.getCron().equals("")) {
            //使用Quartz2以下版本时的代码
            //            CronTrigger trigger = new CronTrigger(TIGGERNAMEPREFIX + scheduleModel.getTaskId(),
            // TIGGERGROUPNAME);
            //            CronExpression cexp = new CronExpression(scheduleModel.getCron());
            //            trigger.setCronExpression(cexp);
            //            return trigger;
            Trigger trigger;
            Date startTime = scheduleModel.getStartTime();
            if(startTime == null){
                startTime = new Date();
            }
            trigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(TIGGERNAMEPREFIX + scheduleModel.getTaskId(), TIGGERGROUPNAME)
                    .startAt(startTime)
                    .withSchedule(CronScheduleBuilder.cronSchedule(scheduleModel.getCron())
                            .withMisfireHandlingInstructionIgnoreMisfires() //忽略异常情况
                    )
                    .build();
            return trigger;
        }
        return null;
    }

    /**
     * 构建一个简单的定时表达式的触发器
     *
     * @param scheduleModel
     *
     * @return simpleTrigger
     *
     * @throws ParseException
     */
    private Trigger builderSimpleTrigger(ScheduleModel scheduleModel) throws ParseException {
        if (scheduleModel.getType() != null && scheduleModel.getType().equalsIgnoreCase("SIMPLE") || scheduleModel
                .getType().contains("SIMPLE")) {
            Trigger trigger;
            Date startTime = scheduleModel.getStartTime();
            if(startTime == null){
                startTime = new Date();
            }
            trigger = TriggerBuilder
                    .newTrigger()
                    .withIdentity(TIGGERNAMEPREFIX + scheduleModel.getTaskId(), TIGGERGROUPNAME)
                    .startAt(startTime)
                    .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                            .withIntervalInSeconds(scheduleModel.getInterval()) //时间间隔
                            .withRepeatCount(scheduleModel.getRepeat())        //重复次数(将执行6次)
                    )
                    .build();
            return trigger;
        }
        return null;
    }

    /**
     * 同步到数据库
     *
     * @throws Exception
     */
    private synchronized void synchronizationToDB() throws Exception {
        String xmlData = XmlUtil.listconvertToXml(schedules, ScheduleModel.class);
        FrameDataUtil.saveFrameData(GlobalConstant.QUARTZXMLDATANAME, xmlData); //刷新到数据库
    }

    /**
     * 获取schedule.xml文件中任务的定义
     * 获取所有任务列表
     *
     * @return
     *
     * @throws Exception
     */
    public List<ScheduleModel> getScheduleModels() throws Exception {
        if (isInit) {
            return schedules;
        }
        String str;
        try {
            str = FrameDataUtil.getDataByName(GlobalConstant.QUARTZXMLDATANAME);
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine读取数据错误!", e);
            throw e;
        }
        if (str == null || str.equals("")) {
            return new ArrayList<>();
        }
        List<ScheduleModel> schedules;
        try {
            schedules = XmlUtil.xmlconvertToList(str, ScheduleModel.class);
            return schedules;
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎解析数据错误!", e);
            throw e;
        }
    }

    /**
     * 添加作业
     *
     * @param schedule
     *
     * @throws Exception
     */
    public void addSchedule(ScheduleModel scheduleModel) throws Exception {
        // 添加数据到列表中并刷新到数据库
        try {
            for (ScheduleModel item : schedules) {
                if (item.getTaskId().equalsIgnoreCase(scheduleModel.getTaskId())) {
                    return;
                }
            }
            schedules.add(scheduleModel);
            synchronizationToDB();
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎添加任务出现错误，写入数据或解析数据发生错误！", e);
            throw e;
        }
        try {
            JobDetail jobDetail = builderJobDetail(scheduleModel);
            Trigger trigger = null;
            if (scheduleModel.getType().equalsIgnoreCase("CRON")) {
                trigger = builderCronTrigger(scheduleModel); //构建cron表达式触发器
            }
            if (scheduleModel.getType().equalsIgnoreCase("SIMPLE")) {
                trigger = builderSimpleTrigger(scheduleModel); //构建SIMPLE表达式触发器
            }
            if (scheduleModel.isRun() && trigger != null) {
                scheduler.scheduleJob(jobDetail, trigger);//任务组合
                log.debug("〖customtask〗计划任务" + scheduleModel.getTaskId() + "--"
                            + "--" + scheduleModel.getTaskName() + "，准备就绪！");
            } else if (trigger == null) {
                log.warn("〖customtask〗计划任务" + scheduleModel.getTaskId() + "--"
                        + "--" + scheduleModel.getTaskName() + "，无法建立定时器！\n"
                        + JsonUtil.objectToJson(scheduleModel));
                }
            scheduler.start();//开始
        } catch (SchedulerException e) {
            log.error("〖customtask〗QuartzEngine引擎启动失败！无法使用定时任务功能！\n"
                    + JsonUtil.objectToJson(scheduleModel), e);
            throw e;
        } catch (Exception e) {
            if (log.isErrorEnabled()) {
                log.error("〖customtask〗QuartzEngine引擎启动任务" + scheduleModel.getTaskId() + "----" + scheduleModel
                        .getTaskName() + "任务失败！！\n"
                        + JsonUtil.objectToJson(scheduleModel), e);
            }
            throw e;
        }
    }

    /**
     * 删除作业
     *
     * @param Taskid 作业号吗
     *
     * @throws Exception
     */
    public void removeSchedule(String taskid) throws Exception {
        //从列表中移除数据并刷新到数据库
        try {
            for (int i = 0; i < schedules.size(); i++) {
                ScheduleModel item = schedules.get(i);
                if (item.getTaskId().equalsIgnoreCase(taskid)) {
                    schedules.remove(item);
                    synchronizationToDB();
                }
            }
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎删除任务出现错误，写入数据或解析数据发生错误！", e);
            throw e;
        }
        try {
            // quartz 2 以下版本使用如下方式删除任务
            // scheduler.pauseTrigger(TIGGERNAMEPREFIX + taskid, TIGGERGROUPNAME);// 停止触发器
            // scheduler.unscheduleJob(TIGGERNAMEPREFIX + taskid, TIGGERGROUPNAME);// 卸载触发器
            // scheduler.pauseJob(DETAILNAMEPREFIX + taskid, DETAILGROUPNAME); //停止任务
            // scheduler.deleteJob(DETAILNAMEPREFIX + taskid, DETAILGROUPNAME);// 删除任务
            TriggerKey tk = new TriggerKey(TIGGERNAMEPREFIX + taskid, TIGGERGROUPNAME);
            JobKey jk = new JobKey(DETAILNAMEPREFIX + taskid, DETAILGROUPNAME);
            scheduler.pauseTrigger(tk);
            scheduler.unscheduleJob(tk);
            scheduler.pauseJob(jk);
            scheduler.deleteJob(jk);
            log.debug("〖customtask〗从QuartzEngine引擎移除任务" + taskid);
        } catch (SchedulerException e) {
            log.error("〖customtask〗QuartzEngine引擎执行删除任务失败！", e);
            throw e;
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎任务调度，移除任务" + taskid + "失败！！", e);
            throw e;
        }
    }

    /**
     * 编辑作业
     *
     * @param schedule
     *
     * @throws Exception
     */
    public void editSchedule(ScheduleModel scheduleModel) throws Exception {
        try {
            removeSchedule(scheduleModel.getTaskId());
            addSchedule(scheduleModel);
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎任务调度，编辑任务" + scheduleModel.getTaskId() + "失败！！", e);
            throw e;
        }
    }

    /**
     * 停止任务
     *
     * @param taskid
     *
     * @throws Exception
     */
    public boolean stopSchedule(String taskid) throws Exception {

        try {
            for (ScheduleModel item : schedules) {
                if (item.getTaskId().equalsIgnoreCase(taskid)) {
                    item.setRun(false);
                    synchronizationToDB();
                }
            }
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎停止任务出现错误，写入数据或解析数据发生错误！", e);
            throw e;
        }
        try {
            // quartz 2以下版本的操作
            // scheduler.pauseTrigger(TIGGERNAMEPREFIX + taskid, TIGGERGROUPNAME);// 停止触发器
            // scheduler.unscheduleJob(TIGGERNAMEPREFIX + taskid, TIGGERGROUPNAME);// 卸载触发器
            // scheduler.pauseJob(DETAILNAMEPREFIX + taskid, DETAILGROUPNAME); //停止任务
            // scheduler.deleteJob(DETAILNAMEPREFIX + taskid, DETAILGROUPNAME);// 删除任务
            TriggerKey tk = new TriggerKey(TIGGERNAMEPREFIX + taskid, TIGGERGROUPNAME);
            JobKey jk = new JobKey(DETAILNAMEPREFIX + taskid, DETAILGROUPNAME);
            scheduler.pauseTrigger(tk);
            scheduler.unscheduleJob(tk);
            scheduler.pauseJob(jk);
            scheduler.deleteJob(jk);
            log.debug("〖customtask〗从QuartzEngine引擎停止任务" + taskid);
            return true;
        } catch (SchedulerException e) {
            log.error("〖customtask〗QuartzEngine引擎执行引停止停任务失败！", e);
            throw e;
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎任务调度，引擎停止任务" + taskid + "失败！！", e);
            throw e;
        }
    }

    /**
     * 暂停任务
     * @param taskid
     * @return
     * @throws Exception
     */
    public boolean pauseSchedule(String taskid) throws Exception {
        try {
            TriggerKey tk = new TriggerKey(TIGGERNAMEPREFIX + taskid, TIGGERGROUPNAME);
            JobKey jk = new JobKey(DETAILNAMEPREFIX + taskid, DETAILGROUPNAME);
            scheduler.pauseTrigger(tk);
            scheduler.unscheduleJob(tk);
            scheduler.pauseJob(jk);
            scheduler.deleteJob(jk);
            log.debug("〖customtask〗从QuartzEngine引擎停止任务" + taskid);
            return true;
        } catch (SchedulerException e) {
            log.error("〖customtask〗QuartzEngine引擎执行引擎停止任务失败！", e);
            throw e;
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎任务调度，引擎停止任务" + taskid + "失败！！", e);
            throw e;
        }
    }

    /**
     * 恢复运行任务
     *
     * @param taskid
     *
     * @throws Exception
     */
    public boolean recoverSchedule(String taskid) throws Exception {
        ScheduleModel scheduleModel = null;
        try {
            for (ScheduleModel item : schedules) {
                if (item.getTaskId().equalsIgnoreCase(taskid) && !item.isRun()) {
                    item.setRun(true);
                    synchronizationToDB();
                    scheduleModel = item;
                }
            }
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎恢复运行任务出现错误，写入数据或解析数据发生错误！", e);
            throw e;
        }
        try {
            if (scheduleModel != null) { //判断是否正在运行
                JobDetail jobDetail = builderJobDetail(scheduleModel);
                Trigger trigger = null;
                if (scheduleModel.getType().equalsIgnoreCase("CRON")) {
                    trigger = builderCronTrigger(scheduleModel); //构建cron表达式触发器
                }
                if (scheduleModel.getType().equalsIgnoreCase("SIMPLE")) {
                    trigger = builderSimpleTrigger(scheduleModel); //构建SIMPLE表达式触发器
                }
                if (scheduleModel.isRun() && trigger != null) {
                    scheduler.scheduleJob(jobDetail, trigger);//任务组合
                    log.debug("〖customtask〗计划任务" + scheduleModel.getTaskId() + "--"
                            + "--" + scheduleModel.getTaskName() + "，准备就绪！");
                } else if (trigger == null) {
                    log.warn("〖customtask〗计划任务" + scheduleModel.getTaskId() + "--"
                            + "--" + scheduleModel.getTaskName() + "，无法建立定时器！\n"
                            + JsonUtil.objectToJson(scheduleModel));

                }
                scheduler.start();//开始
            }
            return true;
        } catch (Exception e) {
            log.error("〖customtask〗QuartzEngine引擎任务调度，恢复任务" + taskid + "失败！！", e);
            throw e;
        }
    }

    /**
     * 获取当前的scheduler
     * @return
     */
    public Scheduler getCurrentScheduler(){
        return scheduler;
    }
}
